use crate::prelude::*;

const NUM_TILES: usize = (SCREEN_WIDTH * SCREEN_HEIGHT) as usize;

#[derive(Copy, Clone, PartialEq)]
pub enum TileType {
    Wall,
    Floor,
    Exit,
}

pub struct Map {
    pub tiles: Vec<TileType>,
    /// 角色是否看到过某个图块
    pub revealed_tiles: Vec<bool>,
}

impl Map{
    pub fn new() -> Self {
        return Self {
            tiles: vec![TileType::Floor; NUM_TILES],
            revealed_tiles: vec![false; NUM_TILES],
        };
    }

    /// 坐标对是否在地图的范围内
    pub fn in_bounds(&self, point: Point) -> bool {
        return (point.x >= 0 && point.x < SCREEN_WIDTH) && (point.y >= 0 && point.y < SCREEN_HEIGHT);
    }

    /// 判断玩家是否可以进入一个图块
    pub fn can_enter_tile(&self, point: Point) -> bool{
        return self.in_bounds(point) && (
            self.tiles[map_idx(point.x, point.y)] == TileType::Floor || 
            self.tiles[map_idx(point.x, point.y)] == TileType::Exit
        );
    }

    /// 如果在地图内，返回该点的索引编号
    pub fn try_idx(&self, point: Point) -> Option<usize>{
        if self.in_bounds(point){
            return Some(map_idx(point.x, point.y));
        }else {
            return None;
        }
    }

    fn valid_exit(&self, loc: Point, delta: Point) -> Option<usize> {
        let destination = loc + delta;
        if self.in_bounds(destination){
            if self.can_enter_tile(destination){
                let idx = self.point2d_to_index(destination);

                return Some(idx);

            }else{
                return None;
            }
        }else{
            return None;
        }
    }
}

/// 计算图块索引编号的公有方法
pub fn map_idx(x: i32, y: i32) -> usize {
    return ((y * SCREEN_WIDTH) + x) as usize;
}

impl Algorithm2D for Map {
    /// 提供了地图的尺寸
    fn dimensions(&self) -> Point {
        return Point::new(SCREEN_WIDTH, SCREEN_HEIGHT);
    }

    /// 判断一个x/y坐标对是否有效，以及是否包含在地图内部
    fn in_bounds(&self, point: Point) -> bool {
        return self.in_bounds(point);
    }
}

impl BaseMap for Map {
    fn get_available_exits(&self, idx: usize) -> SmallVec<[(usize, f32); 10]>{
       let mut exits = SmallVec::new(); 
       let location = self.index_to_point2d(idx);

       if let Some(idx) = self.valid_exit(location, Point::new(-1, 0)){
            exits.push((idx, 1.0))
       }

       if let Some(idx) = self.valid_exit(location, Point::new(1, 0)){
        exits.push((idx, 1.0))
       }

       if let Some(idx) = self.valid_exit(location, Point::new(0, -1)){
        exits.push((idx, 1.0))
       }

       if let Some(idx) = self.valid_exit(location, Point::new(0, 1)){
        exits.push((idx, 1.0))
       }

       return exits;
    }

    fn get_pathing_distance(&self, _idx1: usize, _idx2: usize) -> f32 {
        return DistanceAlg::Pythagoras
            .distance2d(
                self.index_to_point2d(_idx1), 
                self.index_to_point2d(_idx2)
        );
    }

    /// 是否是不透明的
    fn is_opaque(&self, _idx: usize) -> bool {
       return self.tiles[_idx as usize] != TileType::Floor;
    }



}